﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Algobox.JuicyLib.IO.CSV;

namespace Algobox.JuicyLib.Apps.LionsPathCapital
{
    public class LPCTradeFileImporter
    {
        public LPCTradeFileImporter(string sourceName, string hostUri, string user, string password, string archiveDir, string dbConn)
        {
            SOURCE = sourceName;
            FTP_HOST = hostUri;
            FTP_USER = user;
            FTP_PASS = password;

            if (archiveDir[archiveDir.Length - 1] != '/')
                ARCHIVE_DIR = archiveDir + '/';
            else
                ARCHIVE_DIR = archiveDir;

            _FTP = new Algobox.JuicyLib.Net.FTP.JuicyFTP(user, password, hostUri, String.Empty, Algobox.JuicyLib.Net.FTP.JuicyFTP.FTPMode.Windows, Net.FTP.JuicyFTP.FTPCommand.Passive);

            _tradeId = 0;
        }

        readonly string FTP_HOST;
        readonly string FTP_USER;
        readonly string FTP_PASS;
        readonly string ARCHIVE_DIR;
        readonly string FTP_BACKUP_DIR;

        const int FILENAME_DATE_LEN = 8;

        readonly Algobox.JuicyLib.Net.FTP.JuicyFTP _FTP;

        const char SEPERATOR_ROW = '\n';
        const char SEPERATOR_COL = ',';

        /// <summary>
        /// LION.TradeFile.20101001.200227.csv
        /// </summary>
        const string FILE_TRADES = "LION.TradeFile";

        const string FILETYPE_CSV = ".csv";

        readonly string SOURCE;

        int _tradeId;
        Dictionary<string, string> _mapIsinSymbols = new Dictionary<string, string>();

        public void Log(string action, string fileName, string description)
        {
            Console.WriteLine(description);
            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogGeneral(SOURCE, action, fileName, description, null, null, null, null, null, null);
        }

        public void LogError(string message)
        {
            Console.WriteLine(message);
            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(SOURCE, message);
        }

        //
        // MLX is a UNIX FTP server
        //
        // drwx------ 1 mailbox group           1024 Jul 31 11:08 archives
        // -rw------- 1 message group           1097 Jul 31 04:40 confirm.txt.1240.asc.09.07.31_04.40


        public void Run()
        {
            if (!CheckDirectory(String.Empty))
            {
                Log("CheckDirectory", ARCHIVE_DIR, "Archive directory does not exist : " + ARCHIVE_DIR);
                LogError("Archive directory does not exist : " + ARCHIVE_DIR);
                return;
            }

            if (!JuicyLib.Net.FTP.JuicyFTP.IsFTPUri(FTP_HOST))
            {
                Log("CheckURI", FTP_HOST, "FTP host is a malformed Uri : " + FTP_HOST);
                LogError("FTP host is a malformed Uri : " + FTP_HOST);
                return;
            }

            DateTime fileDate = DateTime.Now.Date;
            List<string> fileList;

            if (_FTP.TryGetFileListRecursive("", out fileList))
            {
                foreach (string fileName in fileList)
                {
                    fileDate = ProcessFile(fileName);
                }
            }

        }


        private DateTime ProcessFile(string fileName)
        {
            string dateDir;
            string archiveDir = ARCHIVE_DIR;
            string data;
            string baseFilename;
            string date;
            string message;
            DateTime fileDate = DateTime.Now.Date;

            // get/create actual directory for this file
            // depending on its date
            if (GetDirPath(fileName, out dateDir, out date))
            {
                // final destination directory to save this file
                archiveDir += dateDir;
                // final file name to save the file
                baseFilename = Path.GetFileName(fileName);
                
                if (!DateTime.TryParseExact(date, 
                    "yyyyMMdd",
                    System.Globalization.CultureInfo.InvariantCulture,
                    System.Globalization.DateTimeStyles.None,
                    out fileDate))
                {
                    Log("ProcessFile", archiveDir + "/" + baseFilename, "Failed to parse date for " + date);
                    return fileDate;
                }

                bool overwrite = CheckFileExists(archiveDir, baseFilename);
                bool success = false;

                try
                {
                    // don't process already processed files
                    if (Algobox.JuicyLib.SQL.LiquidView.LiquidViewHelper.FileImportIsFileImported(SOURCE, baseFilename))
                        return fileDate;

                    // get the file
                    Stream stream = null;
                    if (_FTP.GetFileAsStream(fileName, out stream))
                    {
                        if (overwrite)
                            Log("ProcessFile", archiveDir + "/" + baseFilename, "Overwrite file : " + archiveDir + "/" + baseFilename);
                        else
                            Log("ProcessFile", archiveDir + "/" + baseFilename, "Save file : " + archiveDir + "/" + baseFilename);

                        try
                        {
                            using (BufferedStream reader = new BufferedStream(stream))
                            {
                                using (FileStream writer = new FileStream(archiveDir + "/" + baseFilename, FileMode.Create))
                                {
                                    byte[] buffer = new byte[4096];
                                    int bytesRead;

                                    while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0)
                                    {
                                        writer.Write(buffer, 0, bytesRead);
                                    }
                                }
                            }
                        }
                        catch
                        {
                            LogError("ProcessFile failed to archive file to " + archiveDir + "/" + baseFilename);
                            return fileDate;
                        }
                        stream.Dispose();
                        
                        if (baseFilename.Contains(FILE_TRADES))
                        {
                            data = File.ReadAllText(archiveDir + "/" + baseFilename);
//#if DEBUG
                            ProcessTradeFile(data, fileDate);
                            Algobox.JuicyLib.SQL.LiquidView.LiquidViewHelper.FileImportRegisterFile(SOURCE, baseFilename);
//#endif
                        }
                        
                        success = true;
                        /*
                        if(!_FTP.BackupFile(fileName, FTP_BACKUP_DIR + dateDir + "/", out message))
                        {
                            LogError("ProcessFile failed to backup file Message(" + message + ")");
                        }
                        */
                    }
                }
                catch { }
                if(!success)
                {
                    Log("ProcessFile", archiveDir + "/" + baseFilename, "Failed to get file : " + archiveDir + "/" + baseFilename);
                    LogError("ProcessFile - Failed to get file : " + archiveDir + "/" + baseFilename);
                }
                
            }
            else
            {
                Log("ProcessFile", archiveDir, "Failed to get dir : " + archiveDir);
            }

            return fileDate;
        }


        private bool GetDirPath(string fileName, out string dirName, out string date)
        {
            if (fileName.Contains(FILE_TRADES))
            {
                date = fileName.Substring(fileName.IndexOf(FILE_TRADES) + FILE_TRADES.Length + 1, 8);
            }
            else
            {
                date = DateTime.Now.ToString("yyyyMMdd");
            }

            dirName = date.Insert(6, "/");
            dirName = dirName.Insert(4, "/");

            if (CheckDirectory(dirName))
                return true;

            return false;
        }
        

        private bool CheckDirectory(string subDirectory)
        {
            if (subDirectory != String.Empty && subDirectory[0] == '/')
                subDirectory = subDirectory.Substring(1);

            string directory = ARCHIVE_DIR + subDirectory;

            try
            {
                if (Directory.Exists(directory))
                    return true;

                Directory.CreateDirectory(directory);
                return true;
            }
            catch (Exception e)
            {
                Log("CheckDirectory", ARCHIVE_DIR, "Create directory failed with message : " + e.Message);
                return false;
            }
        }


        private bool CheckFileExists(string directory, string fileName)
        {
            if (fileName != String.Empty && fileName[0] == '/')
                fileName = fileName.Substring(1);

            if (directory[directory.Length - 1] != '/')
                directory = directory + '/';

            if (File.Exists(directory + fileName))
                return true;

            return false;
        }


        private bool SaveString(string directory, string fileName, string data)
        {
            if (fileName != String.Empty && fileName[0] == '/')
                fileName = fileName.Substring(1);

            if (directory[directory.Length - 1] != '/')
                directory = directory + '/';

            try
            {                
                File.AppendAllText(directory + fileName, data);
                return true;
            }
            catch
            {
                return false;
            }
        }


        private void ProcessTradeFile(string data, DateTime fileDate)
        {
            const int IX_ACCOUNT = 0;
            const int IX_BROKERCODE = 3;
            const int IX_QUANTITY = 4;
            const int IX_PRICE = 5;
            const int IX_ISIN = 8;               
            const int IX_CCY = 11;
            const int IX_SIDE = 12;
            const int IX_TIME = 15;

            Algobox.JuicyLib.SQL.LiquidView.InstrumentsStock stock;
                        
            using (CsvReader csv = new CsvReader(new StringReader(data), true, SEPERATOR_COL, '"', '\0', '#', true))
            {
                string isin, currency, symbol, account, broker, row;
                TimeSpan time;
                long quantity;
                decimal price;          

                string[] csvRow = new string[csv.FieldCount];

                csv.DefaultParseErrorAction = ParseErrorAction.AdvanceToNextLine;
                while (csv.ReadNextRecord())
                {
                    csv.CopyCurrentRecordTo(csvRow);
                    row = String.Join(",", csvRow);
                    try
                    {
                        isin = csv[IX_ISIN];
                        currency = csv[IX_CCY];

                        if (!Int64.TryParse(csv[IX_QUANTITY].Substring(0, csv[IX_QUANTITY].IndexOf(".")), out quantity))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(SOURCE, "Failed to parse quantity DATA({0})", row); 
                            Console.WriteLine("Can't parse quantity:  " + csv[IX_QUANTITY]);
                            continue;
                        }
                        if (!Decimal.TryParse(csv[IX_PRICE], out price))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(SOURCE, "Failed to parse price DATA({0})", row); 
                            Console.WriteLine("Can't parse price:  " + csv[IX_PRICE]);
                            continue;
                        }
                        if (!TimeSpan.TryParse(csv[IX_TIME], out time))
                        {
                            JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(SOURCE, "Failed to parse time DATA({0})", row); 
                            Console.WriteLine("Can't parse date:  " + csv[IX_TIME]);
                            continue;
                        }

                        if (currency == "GBP")
                            price = price / 100;
                        
                        if (csv[IX_SIDE][0] != 'B')
                        {
                            quantity = -quantity;
                        }

                        if (csv[IX_ACCOUNT] == "0LT25")
                        {
                            account = "LTI025";
                        }
                        else
                        {
                            account = "LTI021";
                        }

                        broker = csv[IX_BROKERCODE];


                        if (!_mapIsinSymbols.TryGetValue(isin, out symbol))
                        {
                            if (!JuicyLib.SQL.LiquidView.LiquidViewHelper.TryGetInstrumentStockUS(isin, out stock))
                            {
                                row = String.Format("{0} {1} {2} {3} {4} {5} {6} {7}", fileDate.ToShortDateString(), time, account, isin, currency, quantity, price, broker);

                                JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(SOURCE, "Failed to find US stock Symbol Data({0})", row);
                                Console.WriteLine("Failed to find US stock Symbol: " + row);
                                continue;
                            }
                            symbol = stock.Instrument.Symbol;
                            _mapIsinSymbols.Add(isin, symbol);
                        }
                        row = String.Format("{0} {1} {2} {3} {4} {5} {6} {7}", fileDate.ToShortDateString(), time, account, symbol, currency, quantity, price, broker);
                                                
                        Console.WriteLine("Adding: " + row);

                        string id = String.Format("LPC-{0:yyyyMMddHHmmss}-{1:00000}", DateTime.Now, ++_tradeId);

                        JuicyLib.SQL.LiquidView.LiquidViewHelper.ImportTradeUSStock(SOURCE, id, id, "XNYS", id, id, account, symbol, quantity, price, fileDate + time, broker, true);

                    }
                    catch (Exception e)
                    {
                        JuicyLib.SQL.LiquidView.LiquidViewHelper.LogException(SOURCE, "Exception({1}) Data({0})", row, e.Message);
                        Console.WriteLine("Exception({1}) Data({0})", row, e.Message);
                        continue;
                    }

                   

                }
            }


        }


    }
}
